-module(allocator).
-export([top_recover/2]).

top_recover_alloc([], Allocated, Pid) ->
    Pid ! no,
    top_recover([], Allocated);

top_recover_alloc([Resource|Free], Allocated, Pid) ->
    Pid ! {yes, Resource},
    Resource1 = lookup(Pid, Allocated),
    if 
        Resource1 == null ->
            link(Pid);
        true -> true
    end,
    top_recover(Free, [{Resource, Pid}|Allocated]).

top_recover(Free, Allocated) ->
    receive
        {Pid, alloc} ->
            top_recover_alloc(Free, Allocated, Pid);
        {Pid, {release, Resource}} ->
            Resource1 = lookup(Pid, Allocated),
            if 
                Resource1 == Resource ->
                    unlink(Pid);
                true -> true
            end,
            Allocated1 = delete({Resource, Pid}, Allocated),
            top_recover([Resource|Free], Allocated1);
        {'EXIT', Pid, _} ->
            %% No need to unlink
            gc(Pid, Allocated, Free)
    end.

gc(Pid, Allocated, Free) ->
    Resource = lookup(Pid, Allocated),
    if 
        Resource /= null ->
            Allocated1 = delete({Resource, Pid}, Allocated),
            gc(Pid, Allocated1, [Resource|Free]);
        true ->
            top_recover(Free, Allocated)
    end.

delete({Resource, Pid}, [{Resource, Pid}|Allocated1]) ->
    Allocated1;
delete({Resource, Pid}, [H|Allocated1]) ->
    [H|delete({Resource, Pid}, Allocated1)].

lookup(_, []) ->
    null;
lookup(Pid, [{Resource, Pid}|_]) ->
    Resource;
lookup(Pid, [_|Allocated1]) ->
    lookup(Pid, Allocated1).
    
